
#include <stdio.h>
#include <stdio.h>
#include <time.h>
#include <Windows.h>
#include "cv.h"
#include "highgui.h"

#define Thread          DWORD WINAPI
#define TSK_YIELD()     Sleep(1)        //Sleep(0)      // take care of YIELD
#define TSK_SLEEP(n)    Sleep(n)
#define CREATE_THREAD   CreateThread
#define RESUME_THREAD   ResumeThread
#define EXIT_THREAD     GetExitCodeThread
#define SUSPEND_THREAD  SuspendThread


#if __USE_LOCK_OBJ__
    #define CS              CRITICAL_SECTION
    #define InitCS(a)       InitializeCriticalSection(&(a))
    #define EnterCS(a)      EnterCriticalSection(&(a))
    #define ExitCS(a)       LeaveCriticalSection(&(a))
    #define UnInitCS(a)     DeleteCriticalSection(&(a))
#else
    #define CS              int32
    #define InitCS(a)       //(a) = LCK_create(0)
    #define EnterCS(a)      //LCK_pend((a), 1000)    // 100 : time out
    #define ExitCS(a)       //LCK_post(a)
    #define UnInitCS(a)     //LCK_delete(a)
#endif

#define CREATE_MUTEX                          CreateMutex(NULL, FALSE, NULL);
#define WAIT_MUTEX(x, mode)              WaitForSingleObject(x, mode);
#define RELEASE_MUTEX(x)                    ReleaseMutex(x);
#define CLOSE_MUTEX(x)                        {CloseHandle(x); x = NULL;}


#define    FrameArr_Num        16

typedef struct _ImgPool_T
{
    IplImage      *InputSrc;
    IplImage      *InputHSVSrc;
    IplImage      *InputYCbCrSrc;    
    IplImage      *Input_HPlane;
    IplImage      *Input_SPlane;
    IplImage      *Input_VPlane;
    IplImage      *Input_YPlane;
    IplImage      *Input_CbPlane;
    IplImage      *Input_CrPlane;    
    IplImage      *Input_ModPlane;    
    IplImage      *Input_TmpPlane;
    HANDLE        hMutex;
    int                IsUsable;
}ImgPool_T;

CvCapture *capture = NULL;
IplImage  *InputSrc = NULL;
int       nFrameArrWrIdx = 0;
int       nFrameArrRdIdx = 0;
int       fps = 0;

ImgPool_T     ImgPool[FrameArr_Num] = {{NULL, 0}};








void Erosion(IplImage *pInputSrc, IplImage *pInputDst, int elemet_type)
{
    IplConvKernel   *element = NULL;
    int                    Erosion_type = 0;
    
    if( elemet_type == 0 ){ Erosion_type = CV_SHAPE_RECT; }
    else if( elemet_type == 1){ Erosion_type = CV_SHAPE_CROSS; }
    else if( elemet_type == 2) { Erosion_type = CV_SHAPE_ELLIPSE; }
    
    element = cvCreateStructuringElementEx(3, 3, 1, 1, Erosion_type, NULL);

    /// Apply the erosion operation
    cvErode(pInputSrc, pInputDst, element, 1);
}

void Dilation(IplImage *pInputSrc, IplImage *pInputDst, int elemet_type)
{
    IplConvKernel   *element = NULL;
    int                    dilation_type = 0;
    
    if( elemet_type == 0 ){ dilation_type = CV_SHAPE_RECT; }
    else if( elemet_type == 1){ dilation_type = CV_SHAPE_CROSS; }
    else if( elemet_type == 2) { dilation_type = CV_SHAPE_ELLIPSE; }

    element = cvCreateStructuringElementEx(5, 5, 3, 3, dilation_type, NULL);
    
    /// Apply the dilation operation
    cvDilate(pInputSrc, pInputDst, element, 1);
}

Thread Thread_Function_GrapImg(LPVOID InputParam)
{
    int       key = 0;
    int                     nFrameCount = 0;
    CvFont              font;
    unsigned char    nText[20];  

    cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 1, 1, 0, 3, 1);
    
    while( key != 'q' )
    {
        /* always check */
        WAIT_MUTEX(ImgPool[nFrameArrWrIdx].hMutex, INFINITE);

        /* get a InputSrc */
        InputSrc = cvQueryFrame(capture);

        cvFlip(InputSrc, InputSrc, 1);

        cvCopy(InputSrc, ImgPool[nFrameArrWrIdx].InputSrc, 0);
        cvCvtColor(ImgPool[nFrameArrWrIdx].InputSrc, ImgPool[nFrameArrWrIdx].InputHSVSrc, CV_BGR2HSV);
        cvCvtPixToPlane(ImgPool[nFrameArrWrIdx].InputHSVSrc, 
                                ImgPool[nFrameArrWrIdx].Input_VPlane, ImgPool[nFrameArrWrIdx].Input_SPlane, ImgPool[nFrameArrWrIdx].Input_HPlane, 0);

        cvCvtColor(ImgPool[nFrameArrWrIdx].InputSrc, ImgPool[nFrameArrWrIdx].InputYCbCrSrc, CV_BGR2YUV);
        cvCvtPixToPlane(ImgPool[nFrameArrWrIdx].InputYCbCrSrc, 
                                ImgPool[nFrameArrWrIdx].Input_YPlane, ImgPool[nFrameArrWrIdx].Input_CbPlane, ImgPool[nFrameArrWrIdx].Input_CrPlane, 0);        

        //sprintf(nText, "%d", nFrameCount++);
        //cvPutText(ImgPool[nFrameArrWrIdx].InputSrc, nText, cvPoint(30, 30), &font, cvScalar(255, 0, 0, 0));
        RELEASE_MUTEX(ImgPool[nFrameArrWrIdx].hMutex);
        nFrameArrWrIdx = (++nFrameArrWrIdx) & 0x0000000F;

        //printf("[%s] Frame:0x%X   WriteIdx: %d\n", __FUNCTION__, InputSrc, nFrameArrWrIdx);        
    }

    return 0;
}


Thread Thread_Function_ShowImg(LPVOID InputParam)
{
    int                     key = 0;
    int                     nThreshold = 128;
    int                     nMode = 2;
    long long int       nStart, nEnd;
    int                     i, j, nWidth, nHeight;
    int                     nMin=0, nMax=255;
    
    /* display video */
    cvNamedWindow( "video_H", 0 );
    cvResizeWindow( "video_H", 320, 700);
    cvNamedWindow( "video_S", 0 );
    cvResizeWindow( "video_S", 320, 240);
    cvNamedWindow( "video_V", 0 );
    cvResizeWindow( "video_V", 320, 240);    

    cvCreateTrackbar("Threshold", "video_H", &nThreshold, 255, NULL); 
    cvCreateTrackbar("Dilation", "video_H", &nMode, 4, NULL);
    cvCreateTrackbar("Min", "video_H", &nMin, 255, NULL);
    cvCreateTrackbar("Max", "video_H", &nMax, 255, NULL);

    while( key != 'q' ) 
    {
        /* always check */
        WAIT_MUTEX(ImgPool[nFrameArrRdIdx].hMutex, INFINITE);
        nStart = clock() ;

        cvThreshold(ImgPool[nFrameArrRdIdx].Input_YPlane, ImgPool[nFrameArrRdIdx].Input_YPlane, nThreshold, 255, CV_THRESH_BINARY);

        nWidth = ImgPool[nFrameArrRdIdx].Input_TmpPlane->width;
        nHeight = ImgPool[nFrameArrRdIdx].Input_TmpPlane->height;
        for(j=0 ; j<nHeight ; j++)
        {
            for(i=0 ; i<nWidth ; i++)
            {
                int     nPixVal = ImgPool[nFrameArrRdIdx].Input_CrPlane->imageData[j*nWidth+i] + 128;
                
                if((nMin <= nPixVal) && (nPixVal <= nMax))
                    ImgPool[nFrameArrRdIdx].Input_TmpPlane->imageData[j*nWidth+i] = 255;
                else
                    ImgPool[nFrameArrRdIdx].Input_TmpPlane->imageData[j*nWidth+i] = 0;
            }
        }

        if(4 != nMode)
            Dilation(ImgPool[nFrameArrRdIdx].Input_TmpPlane, ImgPool[nFrameArrRdIdx].Input_ModPlane, nMode);
        else
            Erosion(ImgPool[nFrameArrRdIdx].Input_TmpPlane, ImgPool[nFrameArrRdIdx].Input_ModPlane, 0);

        cvAbsDiff(ImgPool[nFrameArrRdIdx].Input_TmpPlane, ImgPool[nFrameArrRdIdx].Input_ModPlane, ImgPool[nFrameArrRdIdx].Input_ModPlane);

        /* display InputSrc */
        cvShowImage("video_H", ImgPool[nFrameArrRdIdx].Input_TmpPlane);
        cvShowImage("video_S", ImgPool[nFrameArrRdIdx].Input_CrPlane);
        cvShowImage("video_V", ImgPool[nFrameArrRdIdx].Input_ModPlane);        
        key = cvWaitKey(1000 / fps);
        RELEASE_MUTEX(ImgPool[nFrameArrRdIdx].hMutex);
        nFrameArrRdIdx = (++nFrameArrRdIdx) & 0x0000000F;        

        nEnd = clock();
        printf("     [%s] fps: %0.2f\r", __FUNCTION__, 1000.0/(float)(nEnd-nStart));       
    }

    cvDestroyWindow( "video_H" );
    cvDestroyWindow( "video_S" );
    cvDestroyWindow( "video_V" );

    return 0;
}


DWORD    Create_Thread(void **pThreadHandle, LPTHREAD_START_ROUTINE pfFunction)
{
    DWORD     nThreadID;

    *pThreadHandle = CREATE_THREAD(NULL, 
                                  0, 
                                  pfFunction,
                                  NULL,
                                  CREATE_SUSPENDED,
                                  &nThreadID);

    return nThreadID;
}


void Initialize()
{
    int             i = 0;
    int             nDepth = 0;
    int             nChannels = 0;
    CvSize      nSize;
    

    nSize.width = ( int )cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH );
    nSize.height = ( int )cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT );
    nDepth =IPL_DEPTH_8U;
    nChannels = 3;
    
    for(i=0 ; i<FrameArr_Num ; i++)
    {
        ImgPool[i].InputSrc = cvCreateImage(nSize, nDepth, nChannels);
        ImgPool[i].InputHSVSrc = cvCreateImage(nSize, nDepth, nChannels);
        ImgPool[i].InputYCbCrSrc = cvCreateImage(nSize, nDepth, nChannels);        
        ImgPool[i].Input_HPlane = cvCreateImage(nSize, nDepth, 1);
        ImgPool[i].Input_SPlane = cvCreateImage(nSize, nDepth, 1);
        ImgPool[i].Input_VPlane = cvCreateImage(nSize, nDepth, 1);
        ImgPool[i].Input_YPlane = cvCreateImage(nSize, nDepth, 1);
        ImgPool[i].Input_CbPlane = cvCreateImage(nSize, nDepth, 1);
        ImgPool[i].Input_CrPlane = cvCreateImage(nSize, nDepth, 1);        
        ImgPool[i].Input_ModPlane = cvCreateImage(nSize, nDepth, 1);        
        ImgPool[i].Input_TmpPlane = cvCreateImage(nSize, nDepth, 1);
        ImgPool[i].hMutex = CREATE_MUTEX;
        ImgPool[i].IsUsable = 0;
    }
}


void Destroy()
{
    int     i = 0;

    for(i=0 ; i<FrameArr_Num ; i++)
    {
        cvRelease(&(ImgPool[i].InputSrc));
        cvRelease(&(ImgPool[i].InputHSVSrc));
        cvRelease(&(ImgPool[i].InputYCbCrSrc));
        cvRelease(&(ImgPool[i].Input_HPlane));
        cvRelease(&(ImgPool[i].Input_SPlane));
        cvRelease(&(ImgPool[i].Input_VPlane));
        cvRelease(&(ImgPool[i].Input_YPlane));
        cvRelease(&(ImgPool[i].Input_CbPlane));
        cvRelease(&(ImgPool[i].Input_CrPlane));
        cvRelease(&(ImgPool[i].Input_ModPlane));
        cvRelease(&(ImgPool[i].Input_TmpPlane));
        
        if(NULL != ImgPool[i].hMutex)
            CLOSE_MUTEX(ImgPool[i].hMutex);
    }
}

void main(void)
{ 
    DWORD     nThreadID;
    void      *pThreadHandle0 = NULL, *pThreadHandle1 = NULL;

    /* load the AVI file */
    //capture = cvCaptureFromAVI("K:\\Torrent\\test.avi");
    capture = cvCaptureFromCAM(0);
   
    /* always check */
    if( !capture ) return;
   
    /* get fps, needed to set the delay */
    fps = 33;//( int )cvGetCaptureProperty( capture, CV_CAP_PROP_FPS );

    Initialize();
   
    nThreadID = Create_Thread(&pThreadHandle0, Thread_Function_GrapImg);
    if(pThreadHandle0 == NULL)
        printf("Thread Creation Error\n");
    nThreadID = Create_Thread(&pThreadHandle1, Thread_Function_ShowImg);
    if(pThreadHandle1 == NULL)
        printf("Thread Creation Error\n");

    RESUME_THREAD(pThreadHandle0);
    RESUME_THREAD(pThreadHandle1);

    WaitForSingleObject(pThreadHandle0, INFINITE);
    WaitForSingleObject(pThreadHandle1, INFINITE);

    CloseHandle(pThreadHandle0);
    CloseHandle(pThreadHandle1);

    Destroy();

    /* free memory */
    cvReleaseCapture( &capture );
}
